package viper.receive;

/*****************************************
 * Extractor.java
 * ----------------
 * Revision: v.1.0
 * Author: Ji Kim
 * Date Created: 12/13/2009
 * Description:
 * Last Back-Up:
 * Revision History:
 *****************************************/

import java.io.IOException;
import javax.media.*;
import javax.media.protocol.*;
import java.net.*;

import viper.main.StateManager;
import viper.main.ViperHeader;
import viper.send.CallSender;

public class Extractor implements BufferTransferHandler {

	DataSource ds;
	PushBufferStream streams[];
	Buffer buff;
	LiveStream ls;
	Buffer boofer;
	DatagramSocket sock; 
	InetAddress relayIP1, relayIP2, relayIP3;
	int relayPort;
	int pktType = 1;
	byte[] one;
	byte[] two;
	byte[] three; // different diversity type packets
	byte[] destIP = new byte[4];

	public Extractor(DataSource src, InetAddress[] relayIP_Array){
		//DataSource containing the digitized audio stream is a push-type stream
		streams = ((PushBufferDataSource)src).getStreams();

		// There's only a single audio stream in the DataSource
		streams[0].setTransferHandler(this);

		ds = src; //DataSource
		buff = new Buffer(); 
		boofer = new Buffer();

		ls = new LiveStream();

		try{
			sock = new DatagramSocket(StateManager.VIPER_PORT);
		}catch(SocketException se){
			System.err.println("Failed to open socket.");
		}

		this.relayIP1 = relayIP_Array[0];
		this.relayIP2 = relayIP_Array[1];
		this.relayIP3 = relayIP_Array[2];

		//For troubleshooting
		System.out.println("::Relay Host identified as: " + relayIP1 + "/" + relayPort);
		System.out.println("::Relay Host identified as: " + relayIP2 + "/" + relayPort);
		System.out.println("::Relay Host identified as: " + relayIP3 + "/" + relayPort);
	}

	public LiveStream getLiveStream(){
		return ls;
	}

	public PushBufferStream getStream(){
		return streams[0];
	}

	public void start() {
		try {
			ds.start();
		} catch (IOException e) {
			System.err.println(e);
		}
	}

	public void stop() {
		try {
			ds.stop();
		} catch (IOException e) {
			System.err.println(e);
		}
	}

	/** This will get called when there's data pushed from the PushBufferDataSource. */
	public void transferData(PushBufferStream stream) {
		try {
			stream.read(buff);
		} catch (IOException e) {
			System.err.println(e);
			return;
		}
		transmit(buff);
	}

	void transmit(Buffer buffer) {
		byte[] withHeader;
		
		/** TRANSMIT CODE **/

		byte[] toSend = (byte[])buffer.getData();
		System.out.println(toSend.length); //print num of bytes per audio packet payload

		if(pktType==1){
			one = toSend;
			withHeader = addHeader(one, pktType);
			pktType++;
			try{
				sock.send(new DatagramPacket(withHeader,0,withHeader.length,relayIP1, StateManager.VIPER_PORT));
				System.out.println("Sent Packet...");
			}catch(Exception e){
				System.err.println("Failed to send packet, SeqNo: "+buffer.getSequenceNumber());
			}
		}
		
		else{
			three = new byte[toSend.length];
			two = toSend;
			for(int i=0;i<toSend.length;i++){
				three[i]=(byte)(one[i]^two[i]);
				//System.out.println("Pkt:"+buffer.getSequenceNumber()+" "+three[i]);
			}
			
			withHeader = addHeader(two, pktType);
			pktType++;
			try{
				sock.send(new DatagramPacket(withHeader,0,withHeader.length, relayIP2, StateManager.VIPER_PORT));
				System.out.println("Sent Packet...");
			}catch(Exception e){
				System.err.println("Failed to send packet, SeqNo: "+buffer.getSequenceNumber());
			}
			
			withHeader = addHeader(three, pktType);			
			pktType=1;
			try{
				sock.send(new DatagramPacket(withHeader,0,withHeader.length, relayIP3, StateManager.VIPER_PORT));
				System.out.println("Sent Packet...");
			}catch(Exception e){
				System.err.println("Failed to send packet, SeqNo: "+buffer.getSequenceNumber());
			}
		}
		
		//Send buffer size should be the same as receive buffer size
		//This makes assigning packet seq numbers much more straight forward
		if(CallSender.SEQ_NUM++ == CallReceiver.RCV_BUFFER_SZ)
			CallSender.SEQ_NUM = 0;
	}
	
	//Adds VIPER Header to packets to be transmitted to relays
	//Takes data payload to be transmitted as byte[] noHeader
	//and the diversity pkt type as int pktType
	public byte[] addHeader(byte[] noHeader, int pktType){
		byte[] wHeader = new byte[noHeader.length + 6];
		
		/*Format of packet from sender to relay
		 * <Node Type (1)> <Dest IP (4)> <Seq # (1)> <Pkt Type (1)> <Payload>
		 */
		wHeader[0] = ViperHeader.RELAY;
		System.arraycopy(destIP, 0, wHeader, 1, 4);
		wHeader[5] = (byte)CallSender.SEQ_NUM;
		wHeader[6] = (byte)pktType;
		
		return wHeader;
	}

}

